package middleware

import (
	"bytes"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

// RecoveryConfig 恢复中间件配置
type RecoveryConfig struct {
	// 是否禁用堆栈跟踪
	DisableStackAll bool
	// 是否禁用恢复
	DisableRecovery bool
	// 堆栈跟踪大小
	StackSize int
	// 自定义恢复处理函数
	RecoveryHandler func(*gin.Context, interface{})
}

// DefaultRecoveryConfig 默认恢复中间件配置
var DefaultRecoveryConfig = RecoveryConfig{
	DisableStackAll: false,
	DisableRecovery: false,
	StackSize:       4096,
	RecoveryHandler: defaultRecoveryHandler,
}

// Recovery 恢复中间件
func Recovery(config ...RecoveryConfig) gin.HandlerFunc {
	// 使用默认配置或传入的配置
	recoveryConfig := DefaultRecoveryConfig
	if len(config) > 0 {
		recoveryConfig = config[0]
	}

	return func(c *gin.Context) {
		// 如果禁用恢复，则直接跳过
		if recoveryConfig.DisableRecovery {
			c.Next()
			return
		}

		defer func() {
			if err := recover(); err != nil {
				// 检查连接是否已断开
				var brokenPipe bool
				if ne, ok := err.(*net.OpError); ok {
					if se, ok := ne.Err.(*os.SyscallError); ok {
						if strings.Contains(strings.ToLower(se.Error()), "broken pipe") ||
							strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
							brokenPipe = true
						}
					}
				}

				// 如果是断开连接，则简单记录日志
				if brokenPipe {
					zap.L().Error("连接已断开",
						zap.String("path", c.Request.URL.Path),
						zap.String("method", c.Request.Method),
						zap.Any("error", err),
					)
					// 如果客户端已断开连接，则无法写入状态
					c.Abort()
					return
				}

				// 获取堆栈跟踪
				stack := stack(recoveryConfig.StackSize)

				// 获取请求信息
				httpRequest, _ := httputil.DumpRequest(c.Request, false)

				// 记录错误日志
				zap.L().Error("请求处理异常",
					zap.String("path", c.Request.URL.Path),
					zap.String("method", c.Request.Method),
					zap.Any("error", err),
					zap.String("request", string(httpRequest)),
					zap.String("stack", string(stack)),
				)

				// 调用自定义恢复处理函数
				recoveryConfig.RecoveryHandler(c, err)
			}
		}()

		c.Next()
	}
}

// defaultRecoveryHandler 默认恢复处理函数
func defaultRecoveryHandler(c *gin.Context, err interface{}) {
	// 返回500错误
	c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
		"code":    http.StatusInternalServerError,
		"message": "服务器内部错误",
	})
}

// stack 返回堆栈跟踪
func stack(stackSize int) []byte {
	buf := make([]byte, stackSize)
	n := runtime.Stack(buf, false)
	return buf[:n]
}

// CustomRecovery 自定义恢复中间件
func CustomRecovery(handler func(*gin.Context, interface{})) gin.HandlerFunc {
	return Recovery(RecoveryConfig{
		RecoveryHandler: handler,
	})
}

// LogStackRecovery 记录堆栈并恢复中间件
func LogStackRecovery() gin.HandlerFunc {
	return Recovery(RecoveryConfig{
		RecoveryHandler: func(c *gin.Context, err interface{}) {
			// 获取堆栈跟踪
			stack := stack(DefaultRecoveryConfig.StackSize)

			// 记录错误日志
			zap.L().Error("请求处理异常",
				zap.String("path", c.Request.URL.Path),
				zap.String("method", c.Request.Method),
				zap.Any("error", err),
				zap.String("stack", string(stack)),
			)

			// 返回500错误
			c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
				"code":    http.StatusInternalServerError,
				"message": "服务器内部错误",
			})
		},
	})
}

// CaptureError 捕获错误并记录
func CaptureError() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 创建自定义ResponseWriter
		blw := &bodyLogWriter{
			ResponseWriter: c.Writer,
			body:           bytes.NewBufferString(""),
		}
		c.Writer = blw

		// 开始时间
		start := time.Now()

		// 处理请求
		c.Next()

		// 如果有错误，则记录
		if len(c.Errors) > 0 {
			// 获取响应体
			responseBody := blw.body.String()

			// 记录错误日志
			zap.L().Error("请求处理错误",
				zap.String("path", c.Request.URL.Path),
				zap.String("method", c.Request.Method),
				zap.String("ip", c.ClientIP()),
				zap.Int("status", c.Writer.Status()),
				zap.Duration("latency", time.Since(start)),
				zap.Any("errors", c.Errors),
				zap.String("response", responseBody),
			)
		}
	}
}
